package com.heima.wemedia.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.api.client.ArticleClient;
import com.heima.api.dto.ArticleDto;
import com.heima.common.contants.MessageConstants;
import com.heima.common.contants.WmNewsConstants;
import com.heima.common.exception.LogicException;
import com.heima.common.exception.ParamException;
import com.heima.common.util.UserInfoThreadLocal;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.wemedia.dto.NewsAuthDto;
import com.heima.model.wemedia.dto.WmNewsDownOrUpDto;
import com.heima.model.wemedia.dto.WmNewsDto;
import com.heima.model.wemedia.dto.WmNewsPageReqDto;
import com.heima.model.wemedia.pojo.*;
import com.heima.model.wemedia.vo.WmNewsVo;
import com.heima.wemedia.mapper.*;
import com.heima.wemedia.service.WmNewsService;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 自媒体文章表(WmNews)表服务实现类
 *
 * @author dwxw
 * @since 2023-03-09 10:00:52
 */
@Service("wmNewsService")
@Slf4j
public class WmNewsServiceImpl extends ServiceImpl<WmNewsMapper, WmNews> implements WmNewsService {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Resource
    private WmNewsMapper wmNewsMapper;

    @Resource
    private WmMaterialMapper wmMaterialMapper;

    @Resource
    private WmNewsMaterialMapper wmNewsMaterialMapper;

    @Resource
    private WmUserMapper wmUserMapper;

    @Autowired
    private WmChannelMapper wmChannelMapper;

    @Autowired
    private ArticleClient articleClient;

    /**
     * 更新审核结果
     * 根据建议更新新闻的审核状态和原因
     *
     * @param newsId 新闻ID
     * @param suggestion 审核建议，可为"pass"、"review"或"fail"
     * @param reason 审核不通过的原因
     */
    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    public void updateScanResult(Long newsId, String suggestion, String reason) {
        // 根据新闻ID查询新闻对象
        WmNews wmNews = wmNewsMapper.selectById(newsId);
        // 检查新闻对象是否存在，如果不存在则直接返回
        if (ObjectUtil.isEmpty(wmNews)){
            return;
        }
        // 根据不同的审核建议更新新闻的审核状态
        if(StrUtil.equals("pass",suggestion)){
            wmNews.setStatus(WmNewsConstants.WM_NEWS_AUTH_PASS.intValue());
        }else if (StrUtil.equals("review",suggestion)){
            wmNews.setStatus(WmNewsConstants.WM_NEWS_STATUS_ADMIN_AUTH);
        }else {
            wmNews.setStatus(WmNewsConstants.WM_NEWS_AUTH_FAIL.intValue());
        }
        // 更新审核不通过的原因
        wmNews.setReason(reason);
        // 判断新闻对象状态是否是审核通过未发布
        if(wmNews.getStatus() == 4){
            // 判断新闻发布时间是否小于当前时间
            if (wmNews.getPublishTime().before(new Date())){
                wmNews.setStatus(WmNewsConstants.WM_NEWS_STATUS_PUBLISHED);
            }
        }
        // 判断是否已上架
        ArticleDto dto = isGrounding(wmNews);
        // 更新新闻对象
        wmNewsMapper.updateById(wmNews);
        if (ObjectUtil.isNotEmpty(dto)){
            // 远程调用
            articleClient.saveArticle(dto);
        }
    }

    public ArticleDto isGrounding(WmNews wmNews) {

        // 判断是否上架，如果上架，发布到文章
        if (wmNews.getStatus() == 9){
            // 封装参数
            ArticleDto articleDto = new ArticleDto();
            WmUser wmUser = wmUserMapper.selectById(wmNews.getUserId());
            if (ObjectUtil.isNotEmpty(wmUser)){
                //作者名称
                articleDto.setAuthorName(wmUser.getNickname());
            }
            WmChannel wmChannel = wmChannelMapper.selectById(wmNews.getChannelId());
            if (ObjectUtil.isNotEmpty(wmChannel)){
                //频道名称
                articleDto.setChannelName(wmChannel.getName());
            }
            //主键
            articleDto.setId(wmNews.getId());
            //自媒体端文章ID
            articleDto.setNewsId(wmNews.getId());
            //文章标题
            articleDto.setTitle(wmNews.getTitle());
            //文章封面图片
            articleDto.setImages(wmNews.getImages());
            //文章作者ID
            articleDto.setAuthorId(wmNews.getUserId());
            //文章频道ID
            articleDto.setChannelId(wmNews.getChannelId());
            //文章布局类型;0 : 无图 1 :单图 3:多图
            articleDto.setLayout(wmNews.getType());
            //文章标签
            articleDto.setLabels(wmNews.getLabels());
            //文章发布时间
            articleDto.setPublishTime(wmNews.getPublishTime());
            //文章内容
            articleDto.setContent(wmNews.getContent());
            //文章上下架
            articleDto.setEnable(wmNews.getEnable());
            return articleDto;
        }
        return null;
    }


    @Override
    public void submit(WmNewsDto dto) {
        //1. 校验请求参数
        if (dto == null || StringUtils.isEmpty(dto.getTitle())) {
            throw new ParamException(AppHttpCodeEnum.PARAM_REQUIRE);
        }
        if (dto.getTitle().length() > 30) {
            throw new ParamException(AppHttpCodeEnum.PARAM_SPECIFICATION);
        }
        //2. 抽取文章内容中的图片
        //2.1 获取文章封面图片
        List<String> coverImages = dto.getImages();
        //2.2 获取文章内容图片
        List<String> contentImages = extractContentImages(dto.getContent());
        //3. 判断文章类型是否是自动
        if (dto.getType().intValue() == -1) {
            //如果是自动 , 抽取图片作为封面, 设置文章类型 0: 无图 , 1 : 单图 , 3 : 多图
            if (contentImages.size() >= 3) {
                coverImages = contentImages.stream().limit(3).collect(Collectors.toList());
                dto.setType((short) 3);
            } else if (contentImages.size() > 0) {
                coverImages = contentImages.stream().limit(1).collect(Collectors.toList());
                dto.setType((short) 1);
            } else {
                coverImages = Collections.emptyList();
                dto.setType((short) 0);
            }
        }
        //4. 封装数据保存文章数据到文章表
        WmNews wmNews = saveWmNews(dto, coverImages);

        //5. 封装数据保存数据到文章素材的关联表
        //5.1 保存素材封面引用数据
        saveNewsMaterialRelation(coverImages, wmNews.getId(), 0);
        //5.2 保存素材 内容引用数据
        saveNewsMaterialRelation(contentImages, wmNews.getId(), 1);
        // 文章审核
        sendScanMessage(wmNews.getId(), dto, coverImages, contentImages);
    }

    /**
     * 文章图片和内容审核
     */
    private void sendScanMessage(Long id, WmNewsDto dto, List<String> coverImages, List<String> contentImages) {
        // 1、解析文本内容
        String content = extractContentText(dto.getContent());
        content+=dto.getTitle();

        // 2、图片组装
        List<String> urls = CollectionUtil.union(contentImages, coverImages).stream().distinct().collect(Collectors.toList());

        // 3、封装需要检测的数据
        HashMap<String, Object> messageMap = new HashMap<>();
        messageMap.put("newsId",id);
        messageMap.put("text", content);
        messageMap.put("images", urls);

        // 4、发送消息
        rabbitTemplate.convertAndSend(MessageConstants.GREEN_SCAN_EXCHANGE,MessageConstants.NEWS_SCAN_KEY,JSONUtil.toJsonStr(messageMap));
    }

    /**
     * 提取文本内容
     */
    private String extractContentText(String content) {
        return JSONUtil.parseArray(content).toList(Map.class).stream()
                // 过滤type=text的文本内容
                .filter(map -> map.get("type").equals("text"))
                // 获取value文本内容
                .map(map -> map.get("value").toString())
                // 用逗号拼接
                .collect(Collectors.joining(","));
    }

    @Override
    public Page<WmNews> queryList(WmNewsPageReqDto dto) {
        //1. 封装查询条件
        LambdaQueryWrapper<WmNews> wrapper = Wrappers.<WmNews>lambdaQuery();
        //1.1 状态 --- 精确匹配
        if (dto.getStatus() != null) {
            wrapper.eq(WmNews::getStatus, dto.getStatus().intValue());
        }
        //1.2 关键字  --- 模糊查询
        if (StringUtils.isNotEmpty(dto.getKeyword())) {
            wrapper.like(WmNews::getTitle, dto.getKeyword());
        }
        //1.3 频道 ---- 精确查询
        if (dto.getChannelId() != null) {
            wrapper.eq(WmNews::getChannelId, dto.getChannelId());
        }
        //1.4 发布日期 --- 范围查询
        if (dto.getBeginPubdate() != null && dto.getEndPubdate() != null) {
            wrapper.between(WmNews::getPublishTime, dto.getBeginPubdate(), dto.getEndPubdate());
        }
        //1.5 用户ID ---- 精确查询
        wrapper.eq(WmNews::getUserId, UserInfoThreadLocal.get().getId());
        //1.6 分页参数
        Page<WmNews> page = new Page<>(dto.getPage(), dto.getSize());
        //2. 执行查询
        wmNewsMapper.selectPage(page, wrapper);

        return page;
    }

    @Override
    @GlobalTransactional
    public void downOrUp(WmNewsDownOrUpDto dto) {
        //1. 参数校验
        if (dto == null || dto.getId() == null) {
            throw new LogicException(AppHttpCodeEnum.PARAM_REQUIRE);
        }
        if (dto.getEnable() != 0 && dto.getEnable() != 1) {
            throw new LogicException(AppHttpCodeEnum.PARAM_REQUIRE);
        }
        //2. 查询文章详情
        WmNews wmNews = wmNewsMapper.selectById(dto.getId());
        if (wmNews == null) {
            throw new LogicException(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        //3. 判断文章状态 只有已发布的文章可以上下架
        if (wmNews.getStatus() != 9) {
            throw new LogicException(AppHttpCodeEnum.DATA_IS_ENABLE);
        }
        //4. 修改文章上下架状态
        wmNews.setEnable(dto.getEnable().intValue());
        wmNewsMapper.updateById(wmNews);
        // 封装
        ArticleDto articleDto = new ArticleDto();
        articleDto.setId(wmNews.getId());
        articleDto.setEnable(wmNews.getEnable());
        //5、远程调用
        articleClient.updateArticle(articleDto);
    }


    /**
     * 查询文章列表
     *
     * @param dto
     * @return
     */
    @Override
    public Page<WmNewsVo> findList(NewsAuthDto dto) {

        dto.setPage((dto.getPage() - 1) * dto.getSize());

        List<WmNewsVo> wmNewsVoList = wmNewsMapper.findListAndPage(dto);
        int count = wmNewsMapper.findListCount(dto);
        //3.结果返回
        Page<WmNewsVo> page = new Page<>(dto.getPage(), dto.getSize(), count);
        page.setRecords(wmNewsVoList);

        return page;
    }

    /**
     * 查询文章详情
     *
     * @param id
     * @return
     */
    @Override
    public WmNewsVo findWmNewsVo(Integer id) {
        //1.检查参数
        if (id == null) {
            throw new ParamException(AppHttpCodeEnum.PARAM_REQUIRE);
        }
        //2.查询文章信息
        WmNews wmNews = wmNewsMapper.selectById(id);
        if (wmNews == null) {
            throw new LogicException(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        //3.查询用户信息
        WmUser wmUser = wmUserMapper.selectById(wmNews.getUserId());
        //4.封装vo返回
        WmNewsVo vo = new WmNewsVo();
        //属性拷贝
        BeanUtils.copyProperties(wmNews, vo);
        if (wmUser != null) {
            vo.setAuthorName(wmUser.getNickname());
        }
        return vo;
    }

    /**
     * 文章审核，修改状态
     *
     * @param status 2  审核失败  4 审核成功
     * @param dto
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateStatus(Short status, NewsAuthDto dto) {
        //1.检查参数
        if (dto == null || dto.getId() == null) {
            throw new ParamException(AppHttpCodeEnum.PARAM_REQUIRE);
        }
        //2.查询文章信息
        WmNews wmNews = wmNewsMapper.selectById(dto.getId());
        if (wmNews == null) {
            throw new LogicException(AppHttpCodeEnum.DATA_NOT_EXIST);
        }

        //3.修改文章的状态
        wmNews.setStatus(status.intValue());
        if (StringUtils.isNotBlank(dto.getMsg())) {
            wmNews.setReason(dto.getMsg());
        }

        wmNewsMapper.updateById(wmNews);
    }

    private void saveNewsMaterialRelation(List<String> images, Long newsId, Integer type) {
        //如果需要保存的图片集合中没有图片 , 不需要保存
        if (CollectionUtils.isEmpty(images)) {
            return;
        }
        //对图片URL进行去重
        images = images.stream().distinct().collect(Collectors.toList());
        List<WmMaterial> wmMaterialList = wmMaterialMapper.selectList(Wrappers.<WmMaterial>lambdaQuery().in(WmMaterial::getUrl, images));
        List<Long> ids = wmMaterialList.stream().map(wmMaterial -> wmMaterial.getId()).collect(Collectors.toList());

        //判断是否存在缺失素材
        if (images.size() > ids.size()) {
            throw new RuntimeException("存在素材缺失, 请检查之后提交");
        }

        wmNewsMaterialMapper.saveNewsMaterialRelation(newsId, ids, type);
    }

    /**
     * 保存文章信息到数据库
     *
     * @param dto
     * @param coverImages
     */
    private WmNews saveWmNews(WmNewsDto dto, List<String> coverImages) {
        WmNews wmNews = new WmNews();
        //属性名称和类型必须要完全一致才能copy成功
        BeanUtils.copyProperties(dto, wmNews);
        // images userId type status enable
        wmNews.setImages(StringUtils.join(coverImages, ","));
        wmNews.setUserId(UserInfoThreadLocal.get().getId());
        wmNews.setType(dto.getType().intValue());
        wmNews.setStatus(dto.getStatus().intValue());
        wmNews.setEnable(1);
        wmNews.setUpdatedTime(new Date());
        //新增
        if (wmNews.getId() == null) {
            wmNews.setCreatedTime(new Date());
            wmNewsMapper.insert(wmNews);
            return wmNews;
        }

        //更新数据库中文章数据
        wmNewsMapper.updateById(wmNews);
        //删除之前文章和素材的关联数据
        wmNewsMaterialMapper.delete(Wrappers.<WmNewsMaterial>lambdaQuery().eq(WmNewsMaterial::getNewsId, wmNews.getId()));

        return wmNews;
    }

    /**
     * 从文章内容中抽取图片
     *
     * @param content
     * @return
     */
    private List<String> extractContentImages(String content) {
        if (StringUtils.isEmpty(content)) {
            return Collections.emptyList();
        }
        //1. 将文章内容的JSON字符串转化为java对象 List<Map>
        List<Map> list = JSONUtil.toList(content, Map.class);
        //2. 遍历获取文章的每一个段落数据 Map
        List<String> imageList = list.stream()
                //filter : 过滤, 条件为true 留下, 为false丢弃
                .filter(map -> "image".equals(map.get("type")))
                //map : 转换 , 从一个数据类型, 转换为另一个数据类型
                .map(map -> map.get("value").toString())
                //collect : 数据采集, 将流中的数据采集成指定类型
                .collect(Collectors.toList());

        return imageList;
    }

}

